
#include <stdint.h>
#include "stm32f10x.h"
#include <rthw.h>
#include <rtthread.h>
#include "board.h"


/*--------------- systick init area -------------------------*/

#define _SCB_BASE       (0xE000E010UL)
#define _SYSTICK_CTRL   (*(rt_uint32_t *)(_SCB_BASE + 0x0))
#define _SYSTICK_LOAD   (*(rt_uint32_t *)(_SCB_BASE + 0x4))
#define _SYSTICK_VAL    (*(rt_uint32_t *)(_SCB_BASE + 0x8))
#define _SYSTICK_CALIB  (*(rt_uint32_t *)(_SCB_BASE + 0xC))
#define _SYSTICK_PRI    (*(rt_uint8_t  *)(0xE000ED23UL))

static uint32_t _SysTick_Config(rt_uint32_t ticks)
{
    if ((ticks - 1) > 0xFFFFFF)
    {
        return 1;
    }

    _SYSTICK_LOAD = ticks - 1;
    _SYSTICK_PRI = 0xFF;
    _SYSTICK_VAL  = 0;
    _SYSTICK_CTRL = 0x07;

    return 0;
}

/*------------------ heap init area -------------------------*/

#define RT_HEAP_SIZE (1024 * 6)
static uint8_t rt_heap[RT_HEAP_SIZE];

static void *rt_heap_begin_get(void)
{
    return rt_heap;
}

static void *rt_heap_end_get(void)
{
    return rt_heap + RT_HEAP_SIZE;
}


/*--------------- console init area -------------------------*/

#ifdef RT_USING_CONSOLE

#define CONSOLE_UART          USART1
#define CONSOLE_BAUD          115200
static rt_sem_t console_tx_sem;
static  uint8_t console_tx_buf[RT_CONSOLEBUF_SIZE * 2];

#ifdef RT_USING_FINSH
static rt_mailbox_t console_rx_mb;
#endif

int console_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    USART_InitTypeDef USART_InitStruct;
    DMA_InitTypeDef DMA_InitStructure;

    console_tx_sem = rt_sem_create("shtx", 1, RT_IPC_FLAG_FIFO);
    RT_ASSERT(console_tx_sem);

    #ifdef RT_USING_FINSH
    console_rx_mb = rt_mb_create("shrx", 32, RT_IPC_FLAG_FIFO);
    RT_ASSERT(console_rx_mb);
    #endif

    DMA_DeInit(DMA1_Channel4);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(CONSOLE_UART->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)1;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 1;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);
	DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStruct.USART_BaudRate = CONSOLE_BAUD;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(CONSOLE_UART, &USART_InitStruct);
    USART_DMACmd(CONSOLE_UART, USART_DMAReq_Tx, ENABLE);
    USART_Cmd(CONSOLE_UART, ENABLE);

    #ifdef RT_USING_FINSH
    USART_ITConfig(CONSOLE_UART, USART_IT_RXNE, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    #endif

    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    return 0;
}

void rt_hw_console_output(const char *str)
{
    if (str && rt_thread_self())
    {
        int str_length = rt_strnlen(str, RT_CONSOLEBUF_SIZE - 1);
        if (str_length > 0)
        {
            rt_sem_take(console_tx_sem, RT_WAITING_FOREVER);
            rt_memcpy(&console_tx_buf[RT_CONSOLEBUF_SIZE], str, str_length);
            int tx_len = 0;
            for (int i = 0; i < str_length; i ++)
            {
                if (console_tx_buf[i + RT_CONSOLEBUF_SIZE] == '\n')
                {
                    console_tx_buf[tx_len ++] = '\r';
                }
                console_tx_buf[tx_len ++] = console_tx_buf[i + RT_CONSOLEBUF_SIZE];
            }
            DMA1_Channel4->CMAR = (uint32_t)console_tx_buf;
            DMA1_Channel4->CNDTR = tx_len;
            DMA1_Channel4->CCR |= DMA_CCR1_EN;
        }
    }
}

#ifdef RT_USING_FINSH
char rt_hw_console_getchar(void)
{
    /* Note: the initial value of ch must < 0 */
    int ch = -1;
    rt_mb_recv(console_rx_mb, (rt_ubase_t *)&ch, RT_WAITING_FOREVER);
    return ch;
}

void USART1_IRQHandler(void)
{
    rt_interrupt_enter();
    if(USART_GetITStatus(CONSOLE_UART, USART_IT_RXNE) != RESET)
    {
        USART_ClearFlag(CONSOLE_UART, USART_FLAG_RXNE);
        USART_ClearITPendingBit(CONSOLE_UART, USART_IT_RXNE);
        rt_mb_send(console_rx_mb, USART_ReceiveData(CONSOLE_UART));
    }
    rt_interrupt_leave();
}
#endif

void DMA1_Channel4_IRQHandler(void)
{
    rt_interrupt_enter();
    if(DMA_GetITStatus(DMA1_IT_TC4) != RESET)
    {
        DMA_ClearFlag(DMA1_FLAG_TC4);
        DMA_ClearITPendingBit(DMA1_IT_TC4);
        rt_sem_release(console_tx_sem);
        DMA1_Channel4->CCR &= ~DMA_CCR1_EN;
    }
    rt_interrupt_leave();
}

#endif /* RT_USING_CONSOLE */

/*--------------- rt_hw_board_init api area -------------------------*/
#define TICK_HOOK_MAX 4
static void (*tick_hook[TICK_HOOK_MAX])(void *para);
static void *tick_hook_para[TICK_HOOK_MAX];

void rt_hw_board_init(void)
{
    SystemCoreClockUpdate();
    rt_memset(tick_hook, 0, sizeof(tick_hook));
    _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
    rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
}

void cpu_init(void)
{
    /* NVIC config */
    NVIC_SetVectorTable(0x08000000, 0);
    NVIC_SetPriorityGrouping(NVIC_PriorityGroup_4);

    /* GPIO clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA     \
                           | RCC_APB2Periph_GPIOB   \
                           | RCC_APB2Periph_GPIOC   \
                           | RCC_APB2Periph_GPIOD   \
                           | RCC_APB2Periph_GPIOE   \
                           | RCC_APB2Periph_AFIO, ENABLE);

    /* enable SW, disable JTAG */
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

    /* USB clock*/
    RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);

    /* USART1 clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    /* DMA clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    /* ADC clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    /* Timer clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2      \
                           | RCC_APB1Periph_TIM3    \
                           | RCC_APB1Periph_TIM4, ENABLE);

    /* Set PC14 PC15 as general IO */
    // BKP_DeInit();
    // PWR->CR |= 1<<8;     //PWR_BackupAccessCmd(ENABLE);
    // RCC->BDCR &= 0xFFFFFFFE;
    // BKP->CR &= 0xFFFFFFFE; 
    // PWR->CR &= 0xFFFFFEFF; 
}


void cpu_deinit(void)
{
    SysTick->CTRL &= (~SysTick_CTRL_ENABLE_Msk);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, DISABLE);
    ADC_DeInit(ADC1);
    ADC_DeInit(ADC2);
    GPIO_DeInit(GPIOA);
    GPIO_DeInit(GPIOB);
    GPIO_DeInit(GPIOC);
    GPIO_DeInit(GPIOD);
    GPIO_DeInit(GPIOE);
    TIM_DeInit(TIM1);
    TIM_DeInit(TIM2);
    TIM_DeInit(TIM3);
    TIM_DeInit(TIM4);
    USART_DeInit(USART1);
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_USB, ENABLE);
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_USB, DISABLE);
}

void SysTick_Handler(void)
{
    rt_interrupt_enter();
    rt_tick_increase();

    for (int i = 0; i < TICK_HOOK_MAX; i ++)
    {
        if (tick_hook[i])
        {
            tick_hook[i](tick_hook_para);
        }
    }

    rt_interrupt_leave();
}

rt_err_t rt_register_tick_hook(void (*hook)(void *), void *para)
{
    rt_err_t ret = -1;
    if (hook)
    {
        rt_base_t level = rt_hw_interrupt_disable();
        for (int i = 0; i < TICK_HOOK_MAX; i ++)
        {
            if (!tick_hook[i])
            {
                tick_hook_para[i] = para;
                tick_hook[i] = hook;
                ret = 0;
                break;
            }
        }
        rt_hw_interrupt_enable(level);
    }
    return ret;
}

rt_err_t rt_unregister_tick_hook(void (*hook)(void *), void *para)
{
    rt_err_t ret = -1;
    if (hook)
    {
        rt_base_t level = rt_hw_interrupt_disable();
        for (int i = 0; i < TICK_HOOK_MAX; i ++)
        {
            if (tick_hook[i] == hook && tick_hook_para[i] == para)
            {
                tick_hook[i] = RT_NULL;
                tick_hook_para[i] = RT_NULL;
                ret = 0;
                break;
            }
        }
        rt_hw_interrupt_enable(level);
    }
    return ret;
}


void hw_usb_init(void)
{
    NVIC_InitTypeDef NVIC_InitStructure; 

    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* Enable the USB Wake-up interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_Init(&NVIC_InitStructure);

    /* Clear interrupt status flag */
    *(__IO unsigned *)(0x40005C00L + 0x44) = 0;
    /* Control register */
    // *(__IO unsigned *)(0x40005C00L + 0x40) = 0xBF00;

    void cdc_acm_msc_init(uint8_t busid, uint32_t reg_base);
    cdc_acm_msc_init(0, 0x40005C00L);
}

void USBWakeUp_IRQHandler(void)
{
    rt_interrupt_enter();
    EXTI_ClearITPendingBit(EXTI_Line18);
    rt_interrupt_leave();
}

void USB_LP_CAN1_RX0_IRQHandler(void)
{
    void USBD_IRQHandler(uint8_t busid);
    rt_interrupt_enter();
    USBD_IRQHandler(0);
    rt_interrupt_leave();
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    /* User can add his own implementation to report the file name and line number,
        ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
}
#endif /* USE_FULL_ASSERT */
